"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const CryptoJs = require("crypto-js");

const pageSize = 50;
const LYRICS_API_BASE_URL = 'https://lrc.xms.mx';
const ALL_PLAYLISTS_TAG = { id: 'navidrome__all_playlists', title: '在线歌单' };
const ALL_SONGS_TAG = { id: 'navidrome__all_songs', title: '所有歌曲' };
const NCM_IMPORT_CONCURRENCY = 10;
const NCM_MATCH_RESULT_COUNT = 10;
const DURATION_TOLERANCE_SECONDS = 5;

async function httpGet(urlPath, params) {
    var _a;
    const userVariables = (_a = env === null || env === void 0 ? void 0 : env.getUserVariables()) !== null && _a !== void 0 ? _a : {};
    let { url, username, password } = userVariables;
    if (!(url && username && password)) {
        throw new Error("请在插件设置中填写 Navidrome 服务器地址、用户名和密码");
    }
    if (!url.startsWith("http://") && !url.startsWith("https://")) {
        url = `http://${url}`;
    }
    url = url.replace(/\/+$/, "");
    const salt = Math.random().toString(16).slice(2);
    const token = CryptoJs.MD5(`${password}${salt}`).toString(CryptoJs.enc.Hex);
    const baseParams = { u: username, s: salt, t: token, c: "MusicFree", v: "1.16.1", f: "json" };
    const fullUrl = `${url}/rest/${urlPath}`;
    try {
        const response = await axios_1.default.get(fullUrl, { params: { ...baseParams, ...params }, timeout: 20000 });
        if (response.data?.['subsonic-response']?.status === 'failed') {
            const error = response.data['subsonic-response'].error;
            throw new Error(`Navidrome API 错误: ${error.message} (代码: ${error.code})`);
        }
        if (response.status < 200 || response.status >= 300) {
            throw new Error(`Navidrome 服务器请求失败，HTTP 状态码: ${response.status}`);
        }
        return response.data;
    } catch (error) {
        console.error(`Navidrome Plugin: Failed to fetch ${fullUrl}. Error:`, error);
        if (error instanceof Error && error.message.startsWith('Navidrome')) {
            throw error;
        } else {
            throw new Error(`无法连接到 Navidrome 服务器或请求失败: ${error.message}`);
        }
    }
}

function formatMusicItem(navidromeSong) {
    return {
        id: String(navidromeSong.id),
        title: navidromeSong.title || "未知歌曲",
        artist: navidromeSong.artist || "未知艺术家",
        album: navidromeSong.album || "未知专辑",
        artwork: navidromeSong.coverArt,
        duration: navidromeSong.duration,
        _source: 'navidrome'
    };
}

function formatAlbumItem(navidromeAlbum) {
    return {
        id: String(navidromeAlbum.id),
        title: navidromeAlbum.name || navidromeAlbum.title || "未知专辑",
        artist: navidromeAlbum.artist || "未知艺术家",
        artwork: navidromeAlbum.coverArt,
        description: `歌曲数: ${navidromeAlbum.songCount || '?'}`,
    };
}

function formatSheetItem(navidromePlaylist, username = "Navidrome 用户") {
    return {
        id: String(navidromePlaylist.id),
        title: navidromePlaylist.name || "未知播放列表",
        artist: navidromePlaylist.owner || username,
        artwork: navidromePlaylist.coverArt,
        description: `歌曲: ${navidromePlaylist.songCount || '?'}, 时长: ${Math.round((navidromePlaylist.duration || 0) / 60)}分钟`,
        playCount: navidromePlaylist.playCount,
    };
}

function formatNcmMusicItem(ncmSong) {
    var _a, _b;
    const album = ncmSong.al || ncmSong.album;
    const artists = ncmSong.ar || ncmSong.artists;
    return {
        id: `ncm-tmp-${String(ncmSong.id)}`,
        title: ncmSong.name || "未知歌曲",
        artist: (Array.isArray(artists) && artists.length > 0) ? artists.map(a => a.name).join('/') : "未知艺术家",
        album: album?.name || "未知专辑",
        artwork: album?.picUrl,
        duration: ncmSong.dt ? Math.round(ncmSong.dt / 1000) : undefined,
        _source: 'ncm-import',
        _ncmId: String(ncmSong.id)
    };
}

async function searchMusic(query, page, count = pageSize) {
    const offset = (page - 1) * count;
    const actualCount = page === 1 ? count : pageSize;
    const data = await httpGet('search3', { query, songCount: actualCount, songOffset: offset });
    const songs = data?.['subsonic-response']?.searchResult3?.song ?? [];
    return {
        isEnd: songs.length < actualCount,
        data: songs.map(formatMusicItem)
    };
}

async function searchAlbum(query, page) {
    const offset = (page - 1) * pageSize;
    const data = await httpGet('search3', { query, albumCount: pageSize, albumOffset: offset });
    const albums = data?.['subsonic-response']?.searchResult3?.album ?? [];
    return {
        isEnd: albums.length < pageSize,
        data: albums.map(formatAlbumItem)
    };
}

async function searchSheet(query, page, username) {
    const data = await httpGet('getPlaylists', {});
    const playlists = data?.['subsonic-response']?.playlists?.playlist ?? [];
    let filteredPlaylists = playlists;
    if (query && query.trim()) {
        const lowerCaseQuery = query.trim().toLowerCase();
        filteredPlaylists = playlists.filter(p => p.name && p.name.toLowerCase().includes(lowerCaseQuery));
    }
    return {
        isEnd: true,
        data: filteredPlaylists.map(p => formatSheetItem(p, username))
    };
}

async function getAlbumInfoApi(albumItem, page) {
     if (page > 1) {
         return { isEnd: true, musicList: [] };
     }
     const data = await httpGet('getAlbum', { id: albumItem.id });
     const albumData = data?.['subsonic-response']?.album;
     const songs = albumData?.song ?? [];
     const supplementaryAlbumData = formatAlbumItem(albumData || { id: albumItem.id, title: albumItem.title });
     if (albumData?.artist && albumData?.year) {
         supplementaryAlbumData.description = `${albumData.artist} - ${albumData.year}`;
     } else if (albumData?.artist) {
         supplementaryAlbumData.description = albumData.artist;
     }
     return {
         isEnd: true,
         musicList: songs.map(formatMusicItem),
         albumItem: supplementaryAlbumData
     };
}

async function getMusicSheetInfoApi(sheetItem, page) {
    if (sheetItem.id === ALL_SONGS_TAG.id) {
        return await getAllSongsApi(page);
    }
    if (page > 1) {
        return { isEnd: true, musicList: [] };
    }
    const data = await httpGet('getPlaylist', { id: sheetItem.id });
    const playlistData = data?.['subsonic-response']?.playlist;
    const songs = playlistData?.entry ?? [];
    const supplementarySheetData = formatSheetItem(playlistData || { id: sheetItem.id, name: sheetItem.title });
    if (playlistData?.comment) {
        supplementarySheetData.description = playlistData.comment;
    }
    return {
        isEnd: true,
        musicList: songs.map(formatMusicItem),
        sheetItem: supplementarySheetData
    };
}

async function getAllSongsApi(page) {
    const offset = (page - 1) * pageSize;
    try {
        const data = await httpGet('search3', { query: '', songCount: pageSize, songOffset: offset });
        const songs = data?.['subsonic-response']?.searchResult3?.song ?? [];
        const isEnd = songs.length < pageSize;
        return {
            isEnd: isEnd,
            musicList: songs.map(formatMusicItem),
            sheetItem: page === 1 ? { id: ALL_SONGS_TAG.id, title: ALL_SONGS_TAG.title, description: "服务器上的全部音乐" } : undefined
        };
    } catch(e) {
        console.error(`Navidrome Plugin: Failed to fetch all songs (page ${page}) - ${e.message}`);
        return { isEnd: true, musicList: [] };
    }
}

async function getLyricApi(musicItem) {
    if (!musicItem || !musicItem.title) {
        return null;
    }
    const params = { title: musicItem.title };
    if (musicItem.artist && !['unknown artist', 'various artists'].includes(musicItem.artist.toLowerCase())) {
        params.artist = musicItem.artist;
    }
    if (musicItem.album && !['unknown album'].includes(musicItem.album.toLowerCase())) {
        params.album = musicItem.album;
    }
    try {
        const response = await axios_1.default.get(`${LYRICS_API_BASE_URL}/lyrics`, { params: params, responseType: 'text', timeout: 10000 });
        if (response.data && typeof response.data === 'string' && response.data.trim()) {
            if (response.data.toLowerCase().includes('not found') || response.data.length < 10) {
                // Ignore "not found" or very short responses
            } else {
                 return { rawLrc: response.data };
            }
        }
    } catch (error) {
        // Ignore errors, just return null
    }
    return null;
}

async function getRecommendSheetTagsApi() {
    return {
        pinned: [ ALL_SONGS_TAG, ALL_PLAYLISTS_TAG ],
        data: []
    };
}

async function getRecommendSheetsByTagApi(tag, page, username) {
    if (tag.id === ALL_PLAYLISTS_TAG.id) {
        if (page > 1) {
             return { isEnd: true, data: [] };
        }
        const data = await httpGet('getPlaylists', {});
        const playlists = data?.['subsonic-response']?.playlists?.playlist ?? [];
        return { isEnd: true, data: playlists.map(p => formatSheetItem(p, username)) };
    } else if (tag.id === ALL_SONGS_TAG.id) {
        if (page > 1) {
            return { isEnd: true, data: [] };
        }
        const fakeSheet = {
            id: ALL_SONGS_TAG.id,
            title: ALL_SONGS_TAG.title,
            artist: username || "Navidrome",
            artwork: null,
            description: "浏览服务器上的所有歌曲"
        };
        return { isEnd: true, data: [fakeSheet] };
    } else {
        return { isEnd: true, data: [] };
    }
}

async function getMediaSourceApi(musicItem, quality) {
    var _a;
    const userVariables = (_a = env?.getUserVariables()) ?? {};
    let { url, username, password } = userVariables;
    if (!(url && username && password)) {
        console.error("Navidrome Plugin: Missing credentials for getMediaSource.");
        return null;
    }
    if (!url.startsWith("http://") && !url.startsWith("https://")) {
        url = `http://${url}`;
    }
    url = url.replace(/\/+$/, "");
    const salt = Math.random().toString(16).slice(2);
    const token = CryptoJs.MD5(`${password}${salt}`).toString(CryptoJs.enc.Hex);
    const streamUrl = new URL(`${url}/rest/stream`);
    streamUrl.searchParams.append('u', username);
    streamUrl.searchParams.append('s', salt);
    streamUrl.searchParams.append('t', token);
    streamUrl.searchParams.append('c', 'MusicFree');
    streamUrl.searchParams.append('v', '1.16.1');
    streamUrl.searchParams.append('id', String(musicItem.id));
    const finalUrl = streamUrl.toString();
    console.log(`Navidrome Plugin: Generated stream URL for ID ${musicItem.id}: ${finalUrl}`);
    return { url: finalUrl };
}

async function getMusicInfoApi(musicItem) {
    if (musicItem._source === 'navidrome_ncm_artwork') {
        console.log(`Navidrome Plugin: Keeping existing (NCM) artwork for item ID: ${musicItem.id}`);
        return { artwork: musicItem.artwork };
    }

    if (musicItem.artwork && typeof musicItem.artwork === 'string' && musicItem.artwork.startsWith('mf-')) {
        console.log(`Navidrome Plugin: Found MusicFree artwork cache identifier for item ID: ${musicItem.id}. Skipping fetch.`);
        return { artwork: musicItem.artwork };
    }

    if (musicItem._source === 'ncm-import' || musicItem._source === 'ncm') {
         console.log("Navidrome Plugin: (Fallback) Skipping external cover fetch for NCM item.");
         return { artwork: musicItem.artwork };
    }

    if (!musicItem || !musicItem.title) {
        return null;
    }
    try {
        const params = new URLSearchParams();
        params.append('title', musicItem.title);
        if (musicItem.artist && !['unknown artist', 'various artists'].includes(musicItem.artist.toLowerCase())) {
            params.append('artist', musicItem.artist);
        }
        if (musicItem.album && !['unknown album'].includes(musicItem.album.toLowerCase())) {
            params.append('album', musicItem.album);
        }
        const coverApiUrl = `${LYRICS_API_BASE_URL}/cover?${params.toString()}`;
        console.log(`Navidrome Plugin: Fetching external cover art for Navidrome item ${musicItem.id}: ${coverApiUrl}`);
        return { artwork: coverApiUrl };
    } catch (e) {
        console.error(`Navidrome Plugin: Failed to construct/fetch external cover URL - ${e.message}`);
        return null;
    }
}

async function getNcmTrackDetails(trackIds) {
    if (!trackIds || trackIds.length === 0) {
        return [];
    }
    const ncmHeaders = { Referer: "https://music.163.com/", Origin: "https://music.163.com/", "User-Agent": "Mozilla/5.0" };
    const apiUrl = `https://music.163.com/api/song/detail/?ids=[${trackIds.join(",")}]`;
    try {
        const response = await axios_1.default.get(apiUrl, { headers: ncmHeaders, timeout: 15000 });
        if (response.data?.songs?.length > 0) {
            return response.data.songs.map(formatNcmMusicItem);
        }
        console.warn("NCM Import: Invalid response from /api/song/detail", response.data);
        return [];
    } catch (e) {
        console.error(`NCM Import: Failed to fetch track details - ${e.message}`);
        return [];
    }
}

async function getNcmPlaylistTrackIds(id) {
    const ncmHeaders = { Referer: "https://music.163.com/", Origin: "https://music.163.com/", "User-Agent": "Mozilla/5.0" };
    const apiUrl = `https://music.163.com/api/v3/playlist/detail?id=${id}&n=100000`;
    try {
        const response = await axios_1.default.get(apiUrl, { headers: ncmHeaders, timeout: 15000 });
        if (response.data?.playlist?.trackIds) {
            return response.data.playlist.trackIds.map((_) => _.id);
        }
        console.error("NCM Import: Invalid response from /api/v3/playlist/detail", response.data);
        throw new Error("无法获取网易云歌单详情。");
    } catch (e) {
        console.error(`NCM Import: Failed to get playlist track IDs for ${id} - ${e.message}`);
        throw new Error(`获取网易云歌单信息失败: ${e.message}`);
    }
}

async function findAndMergeNcmTrackOnNavidrome(ncmTrack) {
    const query = `${ncmTrack.title} ${ncmTrack.artist}`;
    console.log(`NCM Import: Searching Navidrome for "${query}" (NCM ID: ${ncmTrack._ncmId})`);

    try {
        const searchResult = await searchMusic(query, 1, NCM_MATCH_RESULT_COUNT);

        if (!searchResult.data || searchResult.data.length === 0) {
            console.log(`NCM Import: No Navidrome results found for "${query}"`);
            return null;
        }

        let bestMatch = null;
        let bestMatchScore = -1;

        for (const naviTrack of searchResult.data) {
            let currentScore = 0;
            let titleMatch = false;
            let artistMatch = false;
            let durationMatch = false;

            if (naviTrack.title.toLowerCase() === ncmTrack.title.toLowerCase()) {
                titleMatch = true;
                currentScore += 10;
            }

            if (naviTrack.artist.toLowerCase() === ncmTrack.artist.toLowerCase()) {
                artistMatch = true;
                currentScore += 5;
            }

            if (typeof naviTrack.duration === 'number' && typeof ncmTrack.duration === 'number') {
                if (Math.abs(naviTrack.duration - ncmTrack.duration) <= DURATION_TOLERANCE_SECONDS) {
                    durationMatch = true;
                    currentScore += 8;
                }
            }

            if (titleMatch && artistMatch && durationMatch) {
                 console.log(`NCM Import: Found PERFECT match for "${ncmTrack.title}": ID ${naviTrack.id} (T+A+D)`);
                 bestMatch = naviTrack;
                 break;
            }

            if (titleMatch && artistMatch && bestMatchScore < 15) {
                 console.log(`NCM Import: Found GOOD match for "${ncmTrack.title}": ID ${naviTrack.id} (T+A)`);
                 bestMatch = naviTrack;
                 bestMatchScore = 15;
            }

             if (titleMatch && durationMatch && bestMatchScore < 10) {
                 console.log(`NCM Import: Found DECENT match for "${ncmTrack.title}": ID ${naviTrack.id} (T+D)`);
                 bestMatch = naviTrack;
                 bestMatchScore = 10;
             }
        }

        if (bestMatch) {
            const finalTrackItem = {
                id: bestMatch.id,
                title: bestMatch.title,
                artist: bestMatch.artist,
                album: bestMatch.album,
                artwork: ncmTrack.artwork,
                duration: bestMatch.duration,
                _source: 'navidrome_ncm_artwork'
            };
            return finalTrackItem;
        } else {
            console.log(`NCM Import: No confident Navidrome match found for "${ncmTrack.title}" by ${ncmTrack.artist}`);
            return null;
        }

    } catch (e) {
        console.error(`NCM Import: Error during matching process for "${ncmTrack.title}": ${e.message}`);
        return null;
    }
}

async function processNcmPlaylistImport(id) {
    const trackIds = await getNcmPlaylistTrackIds(id);
    if (trackIds.length === 0) {
        return [];
    }
    let ncmTracks = [];
    const batchSizeNcm = 200;
    for (let i = 0; i < trackIds.length; i += batchSizeNcm) {
        const batchIds = trackIds.slice(i, i + batchSizeNcm);
        const batchResult = await getNcmTrackDetails(batchIds);
        ncmTracks = ncmTracks.concat(batchResult);
    }

    const matchedNavidromeTracks = [];
    const searchPromises = [];
    let completedCount = 0;

    for (const ncmTrack of ncmTracks) {
        const searchPromise = findAndMergeNcmTrackOnNavidrome(ncmTrack)
            .then(mergedTrack => {
                completedCount++;
                if (completedCount % 10 === 0 || completedCount === ncmTracks.length) {
                    console.log(`NCM Import: Match progress: ${completedCount}/${ncmTracks.length}`);
                }
                return mergedTrack;
            })
            .catch(err => {
                console.error(`NCM Import: Error in individual search promise for "${ncmTrack.title}": ${err.message}`);
                return null;
            });
        searchPromises.push(searchPromise);

        if (searchPromises.length >= NCM_IMPORT_CONCURRENCY) {
            const results = await Promise.all(searchPromises);
            results.forEach(track => { if (track) { matchedNavidromeTracks.push(track); }});
            searchPromises.length = 0;
        }
    }
    if (searchPromises.length > 0) {
        const results = await Promise.all(searchPromises);
        results.forEach(track => { if (track) { matchedNavidromeTracks.push(track); }});
    }
    console.log(`NCM Import: Finished matching. Found ${matchedNavidromeTracks.length} corresponding tracks on Navidrome out of ${ncmTracks.length}.`);
    return matchedNavidromeTracks;
}

async function importNcmSheet(urlLike) {
    const matchResult = urlLike.match(/(?:https:\/\/y\.music\.163.com\/m\/playlist\?id=([0-9]+))|(?:https?:\/\/music\.163\.com\/playlist\/([0-9]+)\/.*)|(?:https?:\/\/music.163.com(?:\/#)?\/playlist\?id=(\d+))|(?:^\s*(\d+)\s*$)/);
    if (!matchResult) {
        throw new Error("无法识别的网易云歌单链接或 ID 格式。");
    }
    const id = matchResult[1] || matchResult[2] || matchResult[3] || matchResult[4];
    if (!id) {
        throw new Error("无法从输入中提取有效的网易云歌单 ID。");
    }
    return await processNcmPlaylistImport(id);
}

module.exports = {
    platform: "音海拾贝",
    version: "1.9.5",
    author: '猫头猫 & AI Assistant',
    srcUrl: "",
    cacheControl: "no-cache",

    userVariables: [
        { key: "url", name: "服务器地址 (URL)", desc: "例如: http://192.168.1.100:4533" },
        { key: "username", name: "用户名" },
        { key: "password", name: "密码", type: 'password' },
    ],
    supportedSearchType: ["music", "album", "sheet"],

    hints: {
        importMusicSheet: [ "支持导入网易云歌单。", "导入时会尝试在您的 Navidrome 服务器上查找匹配歌曲。", "【重要】导入速度取决于歌单大小和服务器响应，可能需几分钟。", "【重要】只有成功匹配的歌曲会被导入，且保留网易云封面。", "匹配基于歌曲名+歌手名+时长，请核对结果。", "请输入网易云歌单分享链接或纯数字 ID。" ],
        importMusicItem: []
    },

    async search(query, page, type) {
         const userVariables = env?.getUserVariables() ?? {};
         try {
             if (type === "music") { return await searchMusic(query, page); }
             if (type === "album") { return await searchAlbum(query, page); }
             if (type === "sheet") { return await searchSheet(query, page, userVariables.username); }
             return { isEnd: true, data: [] };
         } catch (e) {
             console.error(`Navidrome Plugin: Exported search failed - ${e.message}`);
             return { isEnd: true, data: [] };
         }
    },
    async getAlbumInfo(albumItem, page) {
         try {
             return await getAlbumInfoApi(albumItem, page);
         } catch (e) {
             console.error(`Navidrome Plugin: Exported getAlbumInfo failed - ${e.message}`);
             return { isEnd: true, musicList: [] };
         }
    },
    async getMusicSheetInfo(sheetItem, page) {
         try {
             return await getMusicSheetInfoApi(sheetItem, page);
         } catch (e) {
             console.error(`Navidrome Plugin: Exported getMusicSheetInfo failed - ${e.message}`);
             return { isEnd: true, musicList: [] };
         }
    },
    async getMediaSource(musicItem, quality) {
        console.log(`Navidrome Plugin: EXPORTED getMediaSource called - ID: ${musicItem.id}, Quality: ${quality}, Source: ${musicItem._source}`);
        try {
            return await getMediaSourceApi(musicItem, quality);
        } catch (e) {
            console.error(`Navidrome Plugin: Exported getMediaSource failed - ${e.message}`);
            return null;
        }
    },
    async getLyric(musicItem) {
        try {
            return await getLyricApi(musicItem);
        } catch (e) {
            console.error(`Navidrome Plugin: Exported getLyric failed - ${e.message}`);
            return null;
        }
    },
    async getRecommendSheetTags() {
        try {
            return await getRecommendSheetTagsApi();
        } catch (e) {
            console.error(`Navidrome Plugin: Exported getRecommendSheetTags failed - ${e.message}`);
            return { pinned: [], data: [] };
        }
    },
    async getRecommendSheetsByTag(tag, page) {
         const userVariables = env?.getUserVariables() ?? {};
         try {
             return await getRecommendSheetsByTagApi(tag, page, userVariables.username);
         } catch (e) {
             console.error(`Navidrome Plugin: Exported getRecommendSheetsByTag failed - ${e.message}`);
             return { isEnd: true, data: [] };
         }
    },
    async getMusicInfo(musicItem) {
        console.log(`Navidrome Plugin: EXPORTED getMusicInfo called - ID: ${musicItem.id}, Title: ${musicItem.title}, Source: ${musicItem._source}, Artwork: ${musicItem.artwork}`);
        try {
             return await getMusicInfoApi(musicItem);
        } catch (e) {
            console.error(`Navidrome Plugin: Exported getMusicInfo failed - ${e.message}`);
            return null;
        }
    },
    async importMusicSheet(urlLike) {
        console.log("Navidrome Plugin: EXPORTED importMusicSheet called (v1.9.5 - smarter matching)");
        try {
            return await importNcmSheet(urlLike);
        } catch (e) {
             console.error(`Navidrome Plugin: Exported importMusicSheet failed - ${e.message}`);
             throw e;
        }
    }
};